package com.stardata.liangshisq.tcslmon.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.stardata.liangshisq.tcslmon.config.CloudOrderServerProperties;
import com.stardata.liangshisq.tcslmon.config.JobConfigProperties;
import com.stardata.liangshisq.tcslmon.entity.ObjectSyncLog;
import com.stardata.liangshisq.tcslmon.entity.OrderVo;
import com.stardata.liangshisq.tcslmon.entity.SaleDetail;
import com.stardata.liangshisq.tcslmon.mapper.ObjectSyncLogMapper;
import com.stardata.liangshisq.tcslmon.service.*;
import com.stardata.liangshisq.tcslmon.util.CommonConstants;
import com.stardata.liangshisq.tcslmon.util.JSONUtil;
import lombok.AllArgsConstructor;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.util.*;

/**
 * @author Samson Shu
 * @email shush@stardata.top
 * @date 2020/7/31 14:54
 */
@Service
@AllArgsConstructor
@EnableConfigurationProperties({CloudOrderServerProperties.class, JobConfigProperties.class})
public class ObjectSyncLogServiceImpl extends ServiceImpl<ObjectSyncLogMapper, ObjectSyncLog>
    implements ObjectSyncLogService{
  private final Logger logger = LoggerFactory.getLogger(this.getClass());

  private final CloudOrderServerProperties serverProperties;
  private final JobConfigProperties jobConfigProperties;
  private final ObjectSyncLogMapper objectSyncLogMapper;
  private final SyncStatusService syncStatusService;
  private final SimpMessagingTemplate messagingTemplate;
  private final BizBsService bizBsService;
  private final DbiEmpService dbiEmpService;
  private final ItemQueueService itemQueueService;

  @Resource
  private final DataSourceTransactionManager transactionManager;

  private ResponseEntity<String> pushToSyncService(String url, String content) {
    RestTemplate restTemplate = new RestTemplate();
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_JSON);

    HttpEntity<String> request = new HttpEntity<>(content, headers);
    return restTemplate.postForEntity( url, request , String.class );
  }

  @Override
  @NotNull
  public Long queryBizBsIdsForSync(@NotNull Long lastSyncLogId,
                                   @NotNull List<Long> bizBsIdsForSync){
    //1. 查询所有指定ID之后的待同步订单ID
    QueryWrapper<ObjectSyncLog> wrapper = new QueryWrapper<>();
    wrapper.gt("id", lastSyncLogId)
        .eq("object_type", CommonConstants.ObjectType.ORDER);
    List<ObjectSyncLog> logsToSync = objectSyncLogMapper.selectList(wrapper);

    //2. 取得所有待同步的订单ID，并排重
    Long newLastSyncLogId = 0L;
    for (ObjectSyncLog syncLog : logsToSync) {
      Long bizBsId = syncLog.getObjectId();
      if (!bizBsIdsForSync.contains(bizBsId)) {
        bizBsIdsForSync.add(bizBsId);
      }
      if (syncLog.getId() > newLastSyncLogId) {
        newLastSyncLogId = syncLog.getId();
      }
    }
    return newLastSyncLogId;
  }

  @Override
  public void synchronizeBizBs(@NotNull Long newLastSyncLogId, @NotNull List<Long> bizBsIds) {
    //支持关闭订单同步到云端
    if (!jobConfigProperties.getNeedSyncOrder()) return;

    if (bizBsIds.size() == 0) return ;


    //0. 查询待同步的消费者扫码订单信息、销售明细信息
    List<OrderVo> orders = bizBsService.getOrderVosByIds(bizBsIds);
    dbiEmpService.fillServerNameForOrders(orders);

    List<SaleDetail> saleDetails = bizBsService.getSaleDetailsByBsIds(bizBsIds);
    dbiEmpService.fillWaiterCodeForSaleDetails(saleDetails);

    DefaultTransactionDefinition def = new DefaultTransactionDefinition();
    def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
    def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
    TransactionStatus status = transactionManager.getTransaction(def);

    try {

      //1. 同步消费者扫码查看订单信息列表到小程序云服务器
      if (orders.size()>0) {
        String url = serverProperties.getBase() + serverProperties.getOrderSyncService();
        ResponseEntity<String> response = pushToSyncService(url, JSONUtil.toJSONString(orders));
        logger.info(String.format("===> 推送消费者扫码查看订单信息，服务器返回: [%s] ", response));
      }


      //2. 同步销售明细信息列表到小程序云服务器
      if (saleDetails.size()>0) {
        String url = serverProperties.getBase() + serverProperties.getSaleDetailSyncService();
        ResponseEntity<String> response = pushToSyncService(url, JSONUtil.toJSONString(saleDetails));
        logger.info(String.format("===> 推送销售明细信息，服务器返回: [%s] ", response));
      }

      //3. 更新订单最新的ID到 SyncStatus 表
      syncStatusService.updateLastSyncId(newLastSyncLogId,
          CommonConstants.ObjectType.ORDER);

      //4. todo 删除 ObjectSyncLog 中已经处理过的记录

      transactionManager.commit(status);

    }catch (Exception e) {
      transactionManager.rollback(status);
      throw e;
    }

  }

  @NotNull
  @Override
  public Long queryItemNotifications(@NotNull Long lastSyncLogId,
                                     @NotNull List<Map<String, List<Long>>> itemNotifications) {
    //1. 查询所有指定ID之后的待同步订单ID
    QueryWrapper<ObjectSyncLog> wrapper = new QueryWrapper<>();
    wrapper.gt("id", lastSyncLogId)
        .eq("object_type", CommonConstants.ObjectType.ORDER_ITEM);
    List<ObjectSyncLog> logsToSync = objectSyncLogMapper.selectList(wrapper);

    Long newLastSyncLogId = 0L;
    List<Long> newItemIds = new ArrayList<>();
    List<Long> passonItemIds = new ArrayList<>();
    List<Long> cancelPrepareItemIds = new ArrayList<>();
    List<Long> cancelPassonItemIds = new ArrayList<>();

    for (ObjectSyncLog syncLog : logsToSync) {
      Long itemId = syncLog.getObjectId();
      String operType = syncLog.getOperType();

      //2. 处理新增的菜品项（待配菜）
      if (operType.equals("I")) {
        if (!newItemIds.contains(itemId)) {
          newItemIds.add(itemId);
        }
      }

      //3. 处理待传菜的菜品项
      if (operType.equals("P")) {
        if (!passonItemIds.contains(itemId)) {
          passonItemIds.add(itemId);
        }
      }

      //4. 处理配菜中的退单菜品项
      if (operType.equals("D")) {
        if (!cancelPrepareItemIds.contains(itemId)) {
          cancelPrepareItemIds.add(itemId);
        }
      }

      //4. 处理传菜中的退单菜品项
      if (operType.equals("E")) {
        if (!cancelPassonItemIds.contains(itemId)) {
          cancelPassonItemIds.add(itemId);
        }
      }

      //记录最大同步LOG ID
      if (syncLog.getId() > newLastSyncLogId) {
        newLastSyncLogId = syncLog.getId();
      }
    }

    if (newItemIds.size()>0) {
      Map<String, List<Long>> notification = new HashMap<>();
      notification.put("new", newItemIds);
      itemNotifications.add(notification);
    }
    if (passonItemIds.size()>0) {
      Map<String, List<Long>> notification = new HashMap<>();
      notification.put("passon", passonItemIds);
      itemNotifications.add(notification);
    }
    if (cancelPrepareItemIds.size()>0) {
      Map<String, List<Long>> notification = new HashMap<>();
      notification.put("cancel_prepare", cancelPrepareItemIds);
      itemNotifications.add(notification);
    }
    if (cancelPassonItemIds.size()>0) {
      Map<String, List<Long>> notification = new HashMap<>();
      notification.put("cancel_passon", cancelPassonItemIds);
      itemNotifications.add(notification);
    }

    return newLastSyncLogId;
  }

  @Override
  public void notifyKitchen(@NotNull Long newLastSyncLogId,
                            @NotNull List<Map<String, List<Long>>> notifications) {

    //支持关闭通知v1版后厨界面刷新功能
    if (!jobConfigProperties.getNeedNotifyKitchen()) return;

    if(notifications.size()==0) return;

    //1. v1版：将菜品项目变化消息，通过websocket消息发送到相应后厨PC终端
    for (Map<String, List<Long>> notification : notifications) {
      String type = notification.keySet().iterator().next();
      List<Long> itemIds = notification.values().iterator().next();
      if (type.equals("new")) {
        messagingTemplate.convertAndSend("/new_item",
            Objects.requireNonNull(JSONUtil.toJSONString(itemIds)));
      }
      if (type.equals("passon")) {
        messagingTemplate.convertAndSend("/new_passon",
            Objects.requireNonNull(JSONUtil.toJSONString(itemIds)));
      }
      if (type.equals("cancel_prepare")) {
        messagingTemplate.convertAndSend("/cancel_item_prepare",
            Objects.requireNonNull(JSONUtil.toJSONString(itemIds)));
      }
      if (type.equals("cancel_passon")) {
        messagingTemplate.convertAndSend("/cancel_item_passon",
            Objects.requireNonNull(JSONUtil.toJSONString(itemIds)));
      }
    }

    //2. 更新订单最新的ID到 SyncStatus 表
    syncStatusService.updateLastSyncId(newLastSyncLogId,
        CommonConstants.ObjectType.ORDER_ITEM);

    //3. todo 删除 ObjectSyncLog 中已经处理过的记录
  }

  @Override
  public void refreshItemOueue(@NotNull Long newLastSyncLogId,
                               @NotNull List<Map<String, List<Long>>> notifications) {
    //支持关闭刷新配菜窗口菜品排队功能
    if (!jobConfigProperties.getNeedRefreshItemQueue()) return;

    if (notifications.size()<1) return;

    //v2版：只要菜品发生了变化，就刷新配菜窗口排队 (目前是粗放处理，将来支持更细化的逻辑）
    try {
      itemQueueService.refresh();
      messagingTemplate.convertAndSend("/prepare_refresh", "");
      messagingTemplate.convertAndSend("/passon_refresh", "");
    } catch (JsonProcessingException e) {
      logger.error("ERROR ==> 刷新订单排队时出现JSON格式错误", e);
    }

    //3. 更新订单最新的ID到 SyncStatus 表
    syncStatusService.updateLastSyncId(newLastSyncLogId,
        CommonConstants.ObjectType.ORDER_ITEM_QUEUE);

    //4. todo 删除 ObjectSyncLog 中已经处理过的记录
  }
}
